package com.ajouroid.smartcoworker;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.EmbossMaskFilter;
import android.graphics.MaskFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.PorterDuff.Mode;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

public class CustomImageView extends ImageView implements
		ColorPickerDialog.OnAttributeChangedListener {



	// Select Mode(Drawing Or Zooming)
	private Boolean mflag_Mode = false;
	private Boolean mflag_Eraser = false;
	private Boolean mflag_Cur_remove = false;
	private int Cur_remove_type;
	private int mViewType;

	// Matrix
	public Matrix matrix = new Matrix();
	private Matrix savedMatrix = new Matrix();
	private Matrix savedMatrix2 = new Matrix();

	int cnt2 = 0;
	int flag = 0;

	float[] matrixValue = new float[9];
	float originX = 1;
	float originY = 1;
	float orginScale = 1;
	boolean ok = true;
	int ctn = 0;
	int h = 0;
	int w = 0;
	// Zoom Mode
	static final int NONE = 0;
	static final int DRAG = 1;
	static final int ZOOM = 2;
	int touchMode = NONE;

	private static final int WIDTH = 0;
	private static final int HEIGHT = 1;

	private PointF startPoint = new PointF();
	private PointF midPoint = new PointF();
	private float oldDist = 1;
	private float newDist = 1;
	int cnt = 0;

	//

	Context mCtx;
	String id = null;
	String team_id = null;
	String leader = null;

	final String TAG = "CustomImageView";
	private Path mPath;
	private Path mPrePath;
	private Path mRemovePath;
	private Bitmap mCurPageBitmap;
	private Paint mInitPaint;
	private Paint mCurrentPaint;
	private MaskFilter mEmboss;
	private MaskFilter mBlur;
	private Region mboundaray;
	private Region mAllimgBound;
	private Region mEraBoundrary;
	private CustomImageView mSyncPptView;
	private int mPenColor;
	private float mPenThick;
	private int mViewWidth;
	private int mViewHeight;
	private int mImageWidth = 0;
	private int mImageHeight = 0;

	private int mTopMargin = 0;
	private int mLeftMargin = 0;
	int tt = 0;
	int handle = 0;
	int height2 = 0;
	int weight = 0;
	private Slide cslide;
	ArrayList<PathList>[] mPathLists;
	PathList mCurrentPath;
	int mPages;
	int mCurrentPage;

	public CustomImageView(Context context) {
		super(context);
		initial(context);
	}

	public CustomImageView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initial(context);

	}

	public CustomImageView(Context context, AttributeSet attrs, int i) {
		super(context, attrs, i);
		initial(context);
	}

	public void setslide(Slide slide) {
		cslide = slide;
	}

	public void initial(Context context) {

		mboundaray = new Region();
		mAllimgBound = new Region();
		mEraBoundrary = new Region();

		mPenColor = 0xFFFF0000;
		mPenThick = 3;
		mCtx = context;
		mPath = new Path();
		mPrePath = new Path();
		mRemovePath = new Path();

		mInitPaint = new Paint();
		mInitPaint.setAntiAlias(true);
		mInitPaint.setDither(true);
		mInitPaint.setColor(mPenColor);
		mInitPaint.setStyle(Paint.Style.STROKE);
		mInitPaint.setStrokeJoin(Paint.Join.ROUND);
		mInitPaint.setStrokeCap(Paint.Cap.ROUND);
		mInitPaint.setStrokeWidth(mPenThick);
		mInitPaint.setMaskFilter(null);

		mCurrentPaint = new Paint();
		mCurrentPaint.setAntiAlias(true);
		mCurrentPaint.setDither(true);
		mCurrentPaint.setStyle(Paint.Style.STROKE);
		mCurrentPaint.setStrokeJoin(Paint.Join.ROUND);
		mCurrentPaint.setStrokeCap(Paint.Cap.ROUND);

		mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f);
		mBlur = new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL);
		mSyncPptView = (CustomImageView) findViewById(R.id.pm_ppt_view);

	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// TODO Auto-generated method stub
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);

		mViewWidth = View.MeasureSpec.getSize(widthMeasureSpec);
		mViewHeight = View.MeasureSpec.getSize(heightMeasureSpec);
	}

	@Override
	protected void onSizeChanged(int width, int height, int oldWidth,
			int oldHeight) {
		// TODO Auto-generated method stub

		if (mViewWidth > 0)
			mViewWidth = width;
		if (mViewHeight > 0)
			mViewHeight = height;

		Log.d(TAG, "Resized:  " + oldWidth + "," + oldHeight + "->" + width
				+ "," + height);

		if (mCurPageBitmap != null) {
			calImageSize(mCurPageBitmap);
			invalidate();
		}
		super.onSizeChanged(width, height, oldWidth, oldHeight);

		tuningMatrix(matrix, this, true);
		setImagePit();
		setImageMatrix(matrix);
		matrix.getValues(matrixValue);
	}

	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);

		if (mPathLists[mCurrentPage] != null) {
			for (int i = mPathLists[mCurrentPage].size() - 1; i >= 0; i--) {
				PathList pl = mPathLists[mCurrentPage].get(i);
				// delete point
				int size = pl.size();
				if (size == 1) {
					mPathLists[mCurrentPage].remove(pl);
					continue;
				}
				// set thick and color
				float currentThick = pl.getthick() * matrixValue[0];
				mCurrentPaint.setStrokeWidth(currentThick);
				mCurrentPaint.setColor(pl.getcolor());
				if (pl.getType() == 2)// this path is drawed by editview
				{
					if (pl.getType() != mViewType) {
						int a = 100;
						int r = 0;
						int b = 0;
						int g = 0;
						mCurrentPaint.setColor(Color.argb(a, r, g, b));
					}
				} else if (pl.getType() == 1) {
					if (pl.getType() != mViewType) {
						continue;
					}
				}

				mPrePath.reset();
				// Calculate Path
				PointF p = pl.get(0);
				float oldX = (p.x + matrixValue[2] / matrixValue[0])
						* matrixValue[0];
				float oldY = (p.y + matrixValue[5] / matrixValue[4])
						* matrixValue[4];

				mPrePath.moveTo(oldX, oldY);

				for (int j = 1; j < size; j++) {
					p = pl.get(j);
					float x = (p.x + matrixValue[2] / matrixValue[0])
							* matrixValue[0];
					float y = (p.y + matrixValue[5] / matrixValue[4])
							* matrixValue[4];
					mPrePath.quadTo(oldX, oldY, (x + oldX) / 2, (y + oldY) / 2);

					oldX = x;
					oldY = y;
				}
				mPrePath.lineTo(oldX, oldY);
				// set style
				if (pl.getStyle() == 0) {
					mCurrentPaint.setMaskFilter(null);// pen
				} else if(pl.getStyle() ==1){// blur
					setBlur(currentThick);
				}

				if (pl.getEraser() == 1) {// eraser
					Cur_remove_type = pl.getType();
					mflag_Cur_remove = true;// path is drawed by other person
					mRemovePath.reset();
					mRemovePath.set(mPrePath);
					mPathLists[mCurrentPage].remove(pl);
					i = mPathLists[mCurrentPage].size();
					continue;
				}
				// set region
				if (mflag_Eraser == true || mflag_Cur_remove == true) {
					if (Cur_remove_type == pl.getType()) {
						if (mboundaray.setPath(mPrePath, mAllimgBound)) {
							Log.d(TAG, "This boundaray region ");
						} else {
							Log.e(TAG, "fucking boundaray region ");
							mPathLists[mCurrentPage].remove(pl);
							continue;
						}
						if (mEraBoundrary.setPath(mRemovePath, mAllimgBound)) {
							Log.d(TAG, "This eraser region ");
						} else {
							Log.e(TAG, "fucking eraser region ");
						}

						// cal region
						if (mboundaray.quickReject(mEraBoundrary) == false) {
							Log.d(TAG, "eraser remove okokokokokok ");
							mPathLists[mCurrentPage].remove(pl);
							continue;
						} else {// eraser ok but not match so drawing
							canvas.drawPath(mPrePath, mCurrentPaint);
						}
					} else {
						canvas.drawPath(mPrePath, mCurrentPaint);
					}
				} else {// eraser false, drawing
					canvas.drawPath(mPrePath, mCurrentPaint);
				}

			}
		}

		if (mflag_Eraser == false)// sync current drawing
			canvas.drawPath(mPath, mInitPaint);

		mflag_Cur_remove = false;
		mRemovePath.reset();

	}

	public void setViewType(int type) {
		mViewType = type;
	}

	public int getViewType() {
		return mViewType;
	}

	public void setBlur(float thick) {
		mBlur = new BlurMaskFilter(thick, BlurMaskFilter.Blur.NORMAL);
		mCurrentPaint.setMaskFilter(mBlur);
	}

	public void setPenAttribute() {

		new ColorPickerDialog(mCtx, this, mInitPaint).show();
	}

	public void attributeChanged(Paint paint) {
		mInitPaint = paint;
	}

	public Paint getPaint() {
		return mInitPaint;
	}

	public void setEraser(View EraserView) {
		if (mflag_Eraser == false) {
			EraserView.getBackground()
					.setColorFilter(0xaa111111, Mode.SRC_OVER);
			mflag_Eraser = true;
		} else {
			EraserView.getBackground()
					.setColorFilter(0x00000000, Mode.SRC_OVER);
			mflag_Eraser = false;
		}
	}

	public void addPath(PathList pl, int page) {
		if (mPathLists[page] == null)
			mPathLists[page] = new ArrayList<PathList>();

		mPathLists[page].add(pl);

		if (mCurrentPage == page)
			invalidate();
	}

	Dialog customDialog;
	private float mX, mY;
	private static final float TOUCH_TOLERANCE = 4;

	public boolean onTouchEvent(MotionEvent event) {

		matrix.getValues(matrixValue);

		if (mflag_Mode == true && (id.equals(leader) || leader == null)) {
			originX = event.getX();
			originY = event.getY();
			float x = event.getX() / matrixValue[0] - matrixValue[2]
					/ matrixValue[0];
			float y = event.getY() / matrixValue[4] - matrixValue[5]
					/ matrixValue[4];

			JSONArray point = new JSONArray();
			try {
				point.put(x);
				point.put(y);
			} catch (JSONException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				touchStart(x, y);
				invalidate();
				break;
			case MotionEvent.ACTION_MOVE:

				touchMove(x, y);
				if (mflag_Eraser == false)
					invalidate();
				break;
			case MotionEvent.ACTION_UP:
				touchUp();
				invalidate();
				break;
			}
			return true;
		} else if (mflag_Mode == true
				&& (!(id.equals(leader)) && leader != null)) {
			if(flag == 0){
				Toast.makeText(getContext(), "this view is allowed by leader",
						Toast.LENGTH_SHORT).show();
			}
			return true;
		} else {

			// Zoom Point
			CustomImageView view = this;

			switch (event.getAction() & MotionEvent.ACTION_MASK) {
			case MotionEvent.ACTION_DOWN:
				savedMatrix.set(matrix);
				startPoint.set(event.getX(), event.getY());
				touchMode = DRAG;
				break;
			case MotionEvent.ACTION_POINTER_DOWN:
				oldDist = spacingBetweenPoints(event);
				if (oldDist > 10f) {
					savedMatrix.set(matrix);
					calMidPoint(midPoint, event);
					touchMode = ZOOM;
				}
				break;
			case MotionEvent.ACTION_UP:
			case MotionEvent.ACTION_POINTER_UP:
				touchMode = NONE;
				break;
			case MotionEvent.ACTION_MOVE:
				if (touchMode == DRAG) {
					// ...
					matrix.set(savedMatrix);
					matrix.postTranslate(event.getX() - startPoint.x,
							event.getY() - startPoint.y);
				} else if (touchMode == ZOOM) {
					newDist = spacingBetweenPoints(event);
					if (newDist > 10f) {
						matrix.set(savedMatrix);
						float scale = newDist / oldDist;
						matrix.postScale(scale, scale, midPoint.x, midPoint.y);

					}
				}
				break;
			}

			tuningMatrix(matrix, view, false);
			view.setImageMatrix(matrix);

			return true; // indicate event was handled
		}
	}

	private void touchStart(float x, float y) {

		drawStart(originX, originY);

		if (mPathLists[mCurrentPage] == null)
			mPathLists[mCurrentPage] = new ArrayList<PathList>();

		mCurrentPath = new PathList(mImageWidth, mImageHeight);
		mCurrentPath.add(new PointF(x, y));
		mCurrentPath.setcolor(mInitPaint.getColor());
		mCurrentPath.setthick(mInitPaint.getStrokeWidth() / matrixValue[0]);
		mCurrentPath.setType(mViewType);

		if (mInitPaint.getMaskFilter() != null)// 브러쉬
		{
			mCurrentPath.setStyle(1);
			mBlur = new BlurMaskFilter(mInitPaint.getStrokeWidth(),
					BlurMaskFilter.Blur.NORMAL);
			mInitPaint.setMaskFilter(mBlur);

		} else// 그냥 펜일때
		{
			mCurrentPath.setStyle(0);
			mInitPaint.setMaskFilter(null);
		}
		
		if (mflag_Eraser == true) {
			mCurrentPath.setEraser(1);
		}
	}

	private void drawStart(float x, float y) {
		mPath.reset();
		mPath.moveTo(x, y);
		mX = x;
		mY = y;
	}

	private void touchMove(float x, float y) {
		float dx = Math.abs(x - mX);
		float dy = Math.abs(y - mY);
		if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
			drawMove(originX, originY);
			try {
				JSONArray point = new JSONArray();
				point.put(x);
				point.put(y);
				// arrtoPoint.put(point);
			} catch (JSONException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			mCurrentPath.add(new PointF(x, y));
		}
	}

	private void drawMove(float x, float y) {
		mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
		mX = x;
		mY = y;
	}

	private void touchUp() {
		if (mPathLists[mCurrentPage] == null)
			mPathLists[mCurrentPage] = new ArrayList<PathList>();

		mPathLists[mCurrentPage].add(mCurrentPath);

		mPath.lineTo(mX, mY);
		this.invalidate();
		if (mflag_Eraser == true)
			mRemovePath.set(mPath);

		mPath.reset();
		syncPath(team_id, id);
	}

	private float spacingBetweenPoints(MotionEvent event) {
		float x = event.getX(0) - event.getX(1);
		float y = event.getY(0) - event.getY(1);
		return FloatMath.sqrt(x * x + y * y);
	}

	private void calMidPoint(PointF point, MotionEvent event) {
		float x = event.getX(0) + event.getX(1);
		float y = event.getY(0) + event.getY(1);
		point.set(x / 2, y / 2);
	}

	void tuningMatrix(Matrix matrix, ImageView view, Boolean changed) {
		//Matrix Value
		float[] value = new float[9];
		matrix.getValues(value);
		float[] savedValue = new float[9];
		savedMatrix2.getValues(savedValue);

		//View 
		int width = view.getWidth();
		int height = view.getHeight();

		// image 
		Drawable d = view.getDrawable();
		if (d == null)
			return;
		int imageWidth = d.getIntrinsicWidth();
		int imageHeight = d.getIntrinsicHeight();
		
		//scale
		int scaleWidth = (int) (imageWidth * value[0]);
		int scaleHeight = (int) (imageHeight * value[4]);

		// 이미지가 바깥으로 나가지 않도록.
		if (value[2] < width - scaleWidth)
			value[2] = width - scaleWidth;
		if (value[5] < height - scaleHeight)
			value[5] = height - scaleHeight;
		if (value[2] > 0)
			value[2] = 0;
		if (value[5] > 0)
			value[5] = 0;

		if (cnt2 == 1)
			handle = 0;
		// 10배 이상 확대 하지 않도록
		if (value[0] > 10 || value[4] > 10) {
			value[0] = savedValue[0];
			value[4] = savedValue[4];
			value[2] = savedValue[2];
			value[5] = savedValue[5];
		}

		// 화면보다 작게 축소 하지 않도록
		if (imageWidth > width || imageHeight > height) {
			if (scaleWidth + 200 < width && scaleHeight + 200 < height) {
				int target = WIDTH;
				if (imageWidth < imageHeight)
					target = HEIGHT;

				if (target == WIDTH)
					value[0] = value[4] = (float) width / imageWidth;
				if (target == HEIGHT)
					value[0] = value[4] = (float) height / imageHeight;

				scaleWidth = (int) (imageWidth * value[0]);
				scaleHeight = (int) (imageHeight * value[4]);

				if (scaleWidth > width)
					value[0] = value[4] = (float) width / imageWidth;
				if (scaleHeight > height)
					value[0] = value[4] = (float) height / imageHeight;

				if (changed == false) {
					((MainActivity) mCtx).shot(this);
					cnt = 1;
				}
			}
		}
		// 원래부터 작은 얘들은 본래 크기보다 작게 하지 않도록
		else {
			if (value[0] < 1)
				value[0] = 1;
			if (value[4] < 1)
				value[4] = 1;
		}

		scaleWidth = (int) (imageWidth * value[0]);
		scaleHeight = (int) (imageHeight * value[4]);
		// 그리고 가운데 위치하도록 한다.

		if (scaleWidth < width) {
			value[2] = (float) width / 2 - (float) scaleWidth / 2;
		}
		if (scaleHeight < height) {
			value[5] = (float) height / 2 - (float) scaleHeight / 2;
		}

		matrix.setValues(value);
		savedMatrix2.set(matrix);
	}

	public void setImagePit() {

		// 매트릭스 값
		float[] value = new float[9];
		this.matrix.getValues(value);

		// 뷰 크기
		int width = this.getWidth();
		int height = this.getHeight();

		// 이미지 크기
		Drawable d = this.getDrawable();
		if (d == null)
			return;
		int imageWidth = d.getIntrinsicWidth();
		int imageHeight = d.getIntrinsicHeight();
		int scaleWidth = (int) (imageWidth * value[0]);
		int scaleHeight = (int) (imageHeight * value[4]);

		// 이미지가 바깥으로 나가지 않도록.

		value[2] = 0;
		value[5] = 0;

		if (imageWidth > width || imageHeight > height) {
			int target = WIDTH;
			if (imageWidth < imageHeight)
				target = HEIGHT;

			if (target == WIDTH)
				value[0] = value[4] = (float) width / imageWidth;
			if (target == HEIGHT)
				value[0] = value[4] = (float) height / imageHeight;

			scaleWidth = (int) (imageWidth * value[0]);
			scaleHeight = (int) (imageHeight * value[4]);

			if (scaleWidth > width)
				value[0] = value[4] = (float) width / imageWidth;
			if (scaleHeight > height)
				value[0] = value[4] = (float) height / imageHeight;
		}

		// 그리고 가운데 위치하도록 한다.
		scaleWidth = (int) (imageWidth * value[0]);
		scaleHeight = (int) (imageHeight * value[4]);
		if (scaleWidth < width) {
			value[2] = (float) width / 2 - (float) scaleWidth / 2;
		}
		if (scaleHeight < height) {
			value[5] = (float) height / 2 - (float) scaleHeight / 2;
		}

		matrix.setValues(value);

		setImageMatrix(matrix);
	}

	public void setMode(boolean _mode) {
		mflag_Mode = _mode;
	}

	public void setData(String _team_id, String _id, int pages) {
		team_id = _team_id;
		id = _id;
		mPages = pages;
		mPathLists = new ArrayList[pages];
	}

	public void setData(String _team_id, String _leader, String _id, int pages) {
		team_id = _team_id;
		id = _id;
		mPages = pages;
		leader = _leader;
		mPathLists = new ArrayList[pages];
	}

	public synchronized void setImageBitmap(Bitmap bm, int page) {
		super.setImageBitmap(bm);

		mCurPageBitmap = bm;
		calImageSize(bm);
		mCurrentPage = page;
		setImagePit();
		invalidate();
	}

	public void calImageSize(Bitmap bm) {
		float scaleX = (float) mViewWidth / (float) bm.getWidth();
		float scaleY = (float) mViewHeight / (float) bm.getHeight();

		float scale = (scaleX > scaleY) ? scaleY : scaleX;

		mImageWidth = (int) ((float) bm.getWidth() * scale);
		mImageHeight = (int) ((float) bm.getHeight() * scale);

		mAllimgBound = new Region(0, 0, 10000, 10000);

		if (mImageWidth < mViewWidth) {
			mLeftMargin = (mViewWidth - mImageWidth) / 2;
		}

		if (mImageHeight < mViewHeight) {
			mTopMargin = (mViewHeight - mImageHeight) / 2;
		}
	}

	public void setId(String _id) {
		id = _id;
	}

	public void setTeamId(String _team_id) {
		team_id = _team_id;
	}

	public void syncPath(String _team_id, String _actor) {
		JSONObject jsonObj = new JSONObject();
		try {
			JSONArray arr = new JSONArray();
			for (int i = 0; i < mCurrentPath.size(); i++) {
				JSONArray point = new JSONArray();
				PointF p = mCurrentPath.get(i);

				point.put(p.x);
				point.put(p.y);

				arr.put(point);
			}

			jsonObj.put("point", arr);
			jsonObj.put("width", mImageWidth);
			jsonObj.put("height", mImageHeight);
			jsonObj.put("page", mCurrentPage);
			jsonObj.put("color", mCurrentPath.getcolor());
			jsonObj.put("thick", mCurrentPath.getthick());
			jsonObj.put("style", mCurrentPath.getStyle());
			jsonObj.put("eraser", mCurrentPath.getEraser());
			jsonObj.put("type", mCurrentPath.getType());

			PostActionTask task = new PostActionTask();
			task.execute(_team_id, jsonObj.toString());

		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	class PostActionTask extends AsyncTask<String, Void, Void> {

		@Override
		protected Void doInBackground(String... params) {
			HttpConnector conn = new HttpConnector();

			Map<String, String> msgs = new HashMap<String, String>();
			msgs.put("team_id", params[0]);
			msgs.put("action", params[1]);

			conn.post("Action", msgs);

			return null;
		}

	}

}
